#pragma warning( disable : 4786)
#pragma warning( disable : 4503)

#include "tclva.h"

using namespace Tcl;

#ifdef WIN32
extern "C"
{
    __declspec(dllexport) int Tclva_Init(Tcl_Interp* interp);
}
#endif

Tclva::Tclva() :
    _debug(false),
    _pSubscriberListener(0),
    _pSession(0),
    _sessionName(""),
    _endpoint("")
{
	initialize();
}

Tclva::~Tclva() {
    cleanUp();
}

int Tclva::initialize() {
    cout << "[Tclva::initialize] Initializing tclva..." << endl;
    return 0;
}

void Tclva::uninitialize() {
    cout << "[Tclva::uninitialize] Uninitializing tclva..." << endl;
}

void Tclva::connect(Tcl::object const &argv) {
    interpreter i(argv.get_interp(), false);
    // first argument is endpoint
    if(argv.length(i)) {
        object o(argv.at(i, 0));
        _endpoint = o.get<std::string>(i);
    } else {
        cerr << "[Tclva::connect] wrong # args: should be \"connect <endpoint>\"" << endl;
        return;
    }

    object cmd("info hostname");
    object item = i.eval(cmd);
    std::string hostname = item.get<std::string>(i);
    _sessionName = "Subscribe@" + hostname;
    // Below doesn't work, gethostname return 1
    // work around by using Tcl's "info hostname" command
	//makeSessionName("Subscriber", sessName);
    cout << "[Tclva::connect] session name: " << _sessionName.c_str() << endl;

    vhpubsub::SessionFactory *factory = vhpubsub::SessionFactory::GetInstance();
    _pSession = factory->CreateSession(_endpoint.c_str());
    if (_pSession == NULL) {
        cerr << "[Tclva::connect] No Session. Abort." << endl;
        exit(0);
    }

    _pSubscriberListener = new SubscriberListener();
    _pSubscriberListener->setDebugMode(_debug);
    if (_pSubscriberListener == NULL) {
        cerr << "[Tclva::connect] No SubscriberListener instance. Abort." << endl;
        exit(0);
    }
    _pSession->SetName(_sessionName.c_str());
    _pSession->SetMessageListener(_pSubscriberListener);
	_pSession->SetSessionListener(_pSubscriberListener);
	_pSession->Connect();

    getSessionInfo();
}

std::string Tclva::getSessionInfo() {
    std::string info;
    if(_pSession == NULL) {
        cerr << "[Tclva::getSessionInfo] No Session available for this instance. Use Tclva::connect first." << endl;
        return "{}";
    }
    char sessionId[128];
    _pSession->GetId().ToString(sessionId);
    if(_debug) {
        cout << "[Tclva::getSessionInfo]" << endl;
        cout << "    sessionId  : " << sessionId << endl;
        cout << "    sessionName: " << _pSession->GetName() << endl;
        cout << "    sessionInfo: " << _pSession->GetInfo() << endl;
        cout << "    connected  : " << ((_pSession->IsConnected())?"yes":"no") << endl;
    }
    info.append("{");
    info.append("sessionId \"");
    info.append(sessionId);
    info.append("\" sessionName \"");
    info.append(_pSession->GetName());
    info.append("\" sessionInfo \"");
    info.append(_pSession->GetInfo());
    info.append("\" connected \"");
    info.append(((_pSession->IsConnected())?"yes":"no"));
    info.append("\"}");
    if(_debug) {
        cout << "[Tclva::getSessionInfo] returns: " << info.c_str() << endl;
    }
    return info;
}

void Tclva::subscribe(Tcl::object const &argv) {
    std::string vhric;
    interpreter i(argv.get_interp(), false);
    // first argument is VH RIC
    if(argv.length(i)) {
        object o(argv.at(i, 0));
        vhric = o.get<std::string>(i);
    } else {
        cerr << "[Tclva::subscribe] wrong # args: should be \"subscribe <vhric>\"" << endl;
        return;
    }

    if(_pSession == NULL) {
        cerr << "[Tclva::subscribe] No Session available for this instance. Use Tclva::connect first." << endl;
        return;
    }
    std::map<std::string,std::string>::iterator it;
    it = _watchList.find(vhric);
    if(it == _watchList.end()) {
        _pSession->Subscribe(vhric.c_str());
        _watchList.insert( pair<std::string,std::string>(vhric,"") );
        if(_debug)
            cout << "[Tclva::subscribe] " << vhric.c_str() << ", watchlist size: " << _watchList.size() << endl;
    } else {
        cout << "[Tclva::subscribe] " << vhric.c_str() << "already in watchlist." << endl;
    }
}

void Tclva::unsubscribe(Tcl::object const &argv) {
    std::string vhric;
    interpreter i(argv.get_interp(), false);
    // first argument is VH RIC
    if(argv.length(i)) {
        object o(argv.at(i, 0));
        vhric = o.get<std::string>(i);
    } else {
        cerr << "[Tclva::unsubscribe] wrong # args: should be \"unsubscribe <vhric>\"" << endl;
        return;
    }

    if(_pSession == NULL) {
        cerr << "[Tclva::unsubscribe] No Session available for this instance." << endl;
        return;
    }

    std::map<std::string,std::string>::iterator it;
    it = _watchList.find(vhric);
    if(it == _watchList.end()) {
        cout << "[Tclva::unsubscribe] " << vhric << "not in watchlist." << endl;
    } else {
        _pSession->Unsubscribe(vhric.c_str());
        _watchList.erase(it);
        if(_debug)
            cout << "[Tclva::unsubscribe] " << vhric.c_str() << ", watchlist size: " << _watchList.size() << endl;
    }
}

std::string Tclva::getData() {
    std::string r = _pSubscriberListener->getData();
    return r;
}

void Tclva::setDebugMode(Tcl::object const &argv) {
    interpreter i(argv.get_interp(), false);
    std::string debug = "false";
    // first argument is debug mode, ignore the rest
    if(argv.length(i)) {
        object o(argv.at(i, 0));
        debug = o.get<std::string>(i);
    } else {
        cerr << "[Tclva::setDebugMode] wrong # args: should be \"setDebugMode <true|false>\"" << endl;
        return;
    }
    
    if(debug == "true") {
	    _debug = true;
    } else {
        _debug = false;
    }
}

void Tclva::log() {
    cout << "log to implement..." << endl;
}

void Tclva::cleanUp() {
    cout << "[Tclva::cleanUp] ..." << endl;
    try {
        if(_pSession) {
            // gives core dump in Linux
            // omit for now.
            //_pSession->Disconnect();
            _pSession = 0;
        }
    } catch (const runtime_error& e) {
        cerr << "Exception: " << e.what() << endl;
	} catch (...) {
        cerr << "Exception: unknown error" << endl;
	}
    
    if(_pSubscriberListener) {
        delete _pSubscriberListener;
        _pSubscriberListener = 0;
    }
    uninitialize();
}

CPPTCL_MODULE(Tclva, i) {
    i.init_stub("8.4");
    i.pkg_provide("tclva", "6.9.0.23");
    i.eval("namespace eval tclva {}");

    i.class_<Tclva>("tclva")
        .def("connect", &Tclva::connect, variadic())
        .def("getSessionInfo", &Tclva::getSessionInfo)
        .def("subscribe", &Tclva::subscribe, variadic())
        .def("unsubscribe", &Tclva::unsubscribe, variadic())
        .def("getData", &Tclva::getData)
        .def("setDebugMode", &Tclva::setDebugMode, variadic())
        .def("log", &Tclva::log);
}